// HSS.Logging.Management.ConfigurationEditorControl.cs
// ----------------------------------------------------------------------------
// Licensed under the MIT license
// http://www.opensource.org/licenses/mit-license.html
// ----------------------------------------------------------------------------
// HighSpeed-Solutions, LLC
// Copyright (c) 2001-2010
// ----------------------------------------------------------------------------
// File:       ConfigurationEditorControl.cs
// Author:     HSS\gbanta
// Created:    08/12/2010
// Modified:   12/10/2010
// ----------------------------------------------------------------------------
namespace HSS.Logging.Management
{
	#region Using Directives
	using System;
	using System.ComponentModel;
	using System.Diagnostics;
	using System.Threading;
	using System.Windows.Forms;
	using HSS.Data;
	using HSS.Forms;
	using System.ServiceModel;
	#endregion

	#region ConfigurationEditorControl
	/// <summary>
	/// A user interface allowing an administrator to configure the Logging System.
	/// </summary>
	internal sealed partial class ConfigurationEditorControl : DetailModuleBase
	{
		#region Fields

		delegate void SetString(string newStringValue);
		SetString setInstallText;
		SetString setDbText;

		NetNamedPipeBinding binding;
		EndpointAddress epa;

		BusyDialog busyDialog;
		LogConfiguration data;
		string unMaskedConnStr;
		bool hssCoreDbInstalled;
		bool connStrValid;

		static bool showDialog;
		static bool validatingConn;

		const string tempConfigId = "tempEventLog";
		const string defaultDb = "<default>";
		const string masterDb = "master";
		const string installLabel = "Install Log Database";
		const string uninstallLabel = "Uninstall Log Database";

		#endregion

		#region Constructor
		/// <summary>
		/// ctor
		/// </summary>
		public ConfigurationEditorControl()
		{
			InitializeComponent();

			this.binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
#if DEBUG
			this.epa = new EndpointAddress(Log.HSSLogServerURI + "debug");
#else
			this.epa = new EndpointAddress(Log.HSSLogServerURI);
#endif
			setInstallText = new SetString(SetInstallButtonText);
			setDbText = new SetString(SetDbConfigText);
		}
		#endregion

		#region DetailModuleBase Members
		/// <summary>
		/// Initialize the controls logging configuration
		/// </summary>
		/// <param name="Action">not used</param>
		/// <param name="Data">not used</param>
		/// <returns>true if the Child Control successfully initialized into the desired state; else false</returns>
		public override bool InitializeModule(ModuleActions Action, IDataContainer Data)
		{
			try
			{
				using (ILogServerChannel channel = ChannelFactory<ILogServerChannel>.CreateChannel(this.binding, this.epa))
				{
					bool isOpen = false;
					channel.Open();
					isOpen = channel.Ping();
					if (isOpen)
						this.data = channel.GetConfig();
				}
			}
			catch { }

			if (null != this.data)
			{
				this.LoadData();
			}
			else
			{
				this.data = new LogConfiguration("_logConfig", "Log Config", EventLogType.Machine, false, DateTime.Now, 30, string.Empty);
				this.LoadData();
				this.pnlPallet.Hide();
				this.lblNotConnected.Dock = DockStyle.Fill;
			}

			this.CurrentAction = Action;
			this.IsDirty = false;

			this.FormatUI();

			this.ValidateConnection(false);

			return true;
		}
		/// <summary>
		/// Populate the form with the data
		/// </summary>
		public override void LoadData()
		{
			if (null != this.data)
			{
				this.Loading = true;

				if (data.LogType == EventLogType.Database)
				{
					this.rdoLogTypeDb.Checked = true;
					this.btnWriteToLog.Enabled = false;
					this.btnTruncateLog.Enabled = false;
				}
				else
				{
					this.rdoLogTypeMachine.Checked = true;
					this.btnWriteToLog.Enabled = true;
					this.btnTruncateLog.Enabled = false;
				}
				this.chkAutoTruncate.Checked = data.AutoTruncate;
				this.dateTimePicker1.Value = data.TruncateAt;
				this.nudDaysToRetain.Value = (decimal)data.DaysToRetain;
				this.unMaskedConnStr = data.DbConnString;
				this.txtDbConfig.Text = new DataAccessConnection(data.DbConnString).ConnectionStringMasked;
				this.Loading = false;
			}
		}
		/// <summary>
		/// Gets the data contained within the control as a 
		/// LogConfiguration instance casted to an IDataContainer.
		/// </summary>
		/// <returns>An IDataContainer instance of the LogConfiguration.</returns>
		public override IDataContainer GetData()
		{
			LogConfiguration config = new LogConfiguration();

			config.Id = this.data.Id;
			config.Name = this.data.Name;
			config.LogType = (this.rdoLogTypeMachine.Checked ? EventLogType.Machine : EventLogType.Database);
			config.AutoTruncate = this.chkAutoTruncate.Checked;
			config.TruncateAt = this.dateTimePicker1.Value;
			config.DaysToRetain = (int)this.nudDaysToRetain.Value;
			config.DbConnString = this.unMaskedConnStr;

			return config;
		}
		/// <summary>
		/// Persist the configuration changes 
		/// </summary>
		/// <param name="SaveAndNew">not used</param>
		/// <returns>true if save was successful</returns>
		public override bool SaveData(bool SaveAndNew)
		{
			string szErrMsg = string.Empty;
			if (!ValidateData(ref szErrMsg))
			{
				MessageBox.Show(szErrMsg, "Logging Configuration Editor", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return false;
			}
			else
			{
				this.data = GetData() as LogConfiguration;
				using (ILogServerChannel channel = ChannelFactory<ILogServerChannel>.CreateChannel(this.binding, this.epa))
					channel.SetConfig(this.data.Id, this.data.Name, (int)this.data.LogType, this.data.AutoTruncate, this.data.TruncateAt, this.data.DaysToRetain, this.data.DbConnString);
				this.IsDirty = false;
				return true;
			}
		}
		/// <summary>
		/// Validates the data in the control.
		/// </summary>
		/// <param name="errorMessage">The error if any.</param>
		/// <returns>true if successful; otherwise false.</returns>
		protected override bool ValidateData(ref string errorMessage)
		{
			if (this.rdoLogTypeDb.Checked)
			{
				if (string.IsNullOrEmpty(this.txtDbConfig.Text))
				{
					errorMessage = "You must specify a valid database connection.";
					this.txtDbConfig.Focus();
					return false;
				}
			}

			return true;
		}
		#endregion

		#region Methods

		#region Async Helpers
		private void WriteToLogButtonEnable()
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new MethodInvoker(WriteToLogButtonEnable));
				return;
			}
			this.btnWriteToLog.Enabled = !this.IsDirty;
		}
		private void WriteToLogButtonDisable()
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new MethodInvoker(WriteToLogButtonDisable));
				return;
			}
			this.btnWriteToLog.Enabled = false;
		}
		private void TruncateLogButtonEnable()
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new MethodInvoker(TruncateLogButtonEnable));
				return;
			}
			this.btnTruncateLog.Enabled = !this.IsDirty;
		}
		private void TruncateLogButtonDisable()
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new MethodInvoker(TruncateLogButtonDisable));
				return;
			}
			this.btnTruncateLog.Enabled = false; ;
		}
		private void InstallButtonEnable()
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new MethodInvoker(InstallButtonEnable));
				return;
			}
			this.btnInstall.Enabled = true;
		}
		private void InstallButtonDisable()
		{
			if (this.InvokeRequired)
			{
				this.Invoke(new MethodInvoker(InstallButtonDisable));
				return;
			}
			this.btnInstall.Enabled = false; ;
		}
		private void SetInstallButtonText(string newText)
		{
			if (this.InvokeRequired)
			{
				this.Invoke(setInstallText, newText);
				return;
			}
			this.btnInstall.Text = newText;
		}
		private void SetDbConfigText(string newText)
		{
			if (this.InvokeRequired)
			{
				this.Invoke(setDbText, newText);
				return;
			}
			this.txtDbConfig.Text = newText;
		}
		#endregion

		private void ValidateConnection()
		{
			ValidateConnection(true);
		}
		private void ValidateConnection(bool Verbose)
		{
			if (this.rdoLogTypeMachine.Checked)
				return;

			if (this.Loading)
				return;

			if (string.IsNullOrEmpty(this.unMaskedConnStr))
				return;

			if (validatingConn)
				return;

			validatingConn = true;
			this.bgwValidateConn.RunWorkerAsync(Verbose);
			this.ShowBusyDialog(HSS.Logging.Management.Resources.msg_ValidateConnStr);
		}
		private void ValidateConnectionComplete(ValidationResults Results)
		{
			try
			{
				string strErr = Results.Message;
				if (null != Results.Error)
					strErr = Results.Error.Message;

				this.hssCoreDbInstalled = Results.CoreDBInstalled;

				if (!Results.Success)
				{
					#region Failed connection
					this.btnInstall.Enabled = false;
					this.btnInstall.Text = installLabel;
					this.btnWriteToLog.Enabled = false;
					this.btnTruncateLog.Enabled = false;
					this.btnViewLog.Enabled = false;
					this.connStrValid = false;
					if (!Loading)
					{
						this.HideBusyDialog();
						MessageBox.Show(strErr, HSS.Logging.Management.Resources.connectionFailureTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
					}
					#endregion
				}
				else
				{
					#region Success connection

					this.btnInstall.Enabled = true;
					this.connStrValid = true;

					if (Results.ShowVerbose)
					{
						this.HideBusyDialog();
						MessageBox.Show(HSS.Logging.Management.Resources.connectSuccess
						   , HSS.Logging.Management.Resources.connectSucessTitle
						   , MessageBoxButtons.OK
						   , MessageBoxIcon.Information);
					}

					if (this.hssCoreDbInstalled)
					{
						#region Core Installed
						this.btnInstall.Text = uninstallLabel;
						this.btnWriteToLog.Enabled = !this.IsDirty;
						this.btnTruncateLog.Enabled = !this.IsDirty;
						this.btnViewLog.Enabled = !this.IsDirty;
						#endregion
					}
					else
					{
						#region Core not installed
						this.btnInstall.Text = installLabel;
						this.btnWriteToLog.Enabled = false;
						this.btnTruncateLog.Enabled = false;
						this.btnViewLog.Enabled = false;
						if (!Loading)
						{
							this.HideBusyDialog();
							MessageBox.Show(HSS.Logging.Management.Resources.coreSqlServiceNotInstalled
							, HSS.Logging.Management.Resources.coreSqlServiceNotInstalledTitle
							, MessageBoxButtons.OK
							, MessageBoxIcon.Warning);
						}
						#endregion
					}

					#endregion
				}
			}
			finally
			{
				validatingConn = false;
			}
		}

		private void ShowBusyDialog(string Message)
		{
			showDialog = true;
			new Thread(this.ShowBusyDialogProc).Start(Message);
		}
		private void ShowBusyDialogProc(object Message)
		{
			Thread.CurrentThread.IsBackground = true;
			using (this.busyDialog = new BusyDialog(Message as string, true))
			{
				Thread.CurrentThread.Join(900); // Delay in case the time to validate is quick and we don't need to show
				if (null != this.busyDialog && showDialog)
					this.busyDialog.ShowDialog();
			}
			this.Invoke(new MethodInvoker(this.FocusThis));
		}
		private void HideBusyDialog()
		{
			showDialog = false;
			if (null != this.busyDialog)
			{
				if (this.busyDialog.InvokeRequired)
				{
					this.busyDialog.BeginInvoke(new MethodInvoker(this.HideBusyDialog));
					return;
				}
				this.busyDialog.Close();
				this.busyDialog = null;
			}
		}
		private void FocusThis()
		{
			this.Focus();
		}
		private void ShowLogViewer()
		{
			if (this.rdoLogTypeMachine.Checked)
				System.Diagnostics.Process.Start("EventVwr");
			else
			{
				try { LogViewer.Show(true, null); }
				catch (Exception ex)
				{
					Trace.WriteLine(ex.ToString());
					MessageBox.Show(ex.ToString());
				}
			}
		}

		private void FormatUI()
		{
			if (rdoLogTypeMachine.Checked)
			{
				this.pnlDbConfig.Enabled = false;
				this.grbRetentionPolicy.Enabled = false;
				this.btnWriteToLog.Enabled = !this.IsDirty;
				this.btnTruncateLog.Enabled = false;
				this.btnViewLog.Enabled = !this.IsDirty;
			}
			else if (rdoLogTypeDb.Checked)
			{
				this.pnlDbConfig.Enabled = true;
				this.grbRetentionPolicy.Enabled = true;
				this.dateTimePicker1.Enabled = this.chkAutoTruncate.Checked;
				this.nudDaysToRetain.Enabled = this.chkAutoTruncate.Checked;

				if (this.txtDbConfig.Text.Length > 0)
				{
					this.btnValidateConnection.Enabled = !this.IsDirty;
					bool testOk = !this.IsDirty && this.connStrValid && this.hssCoreDbInstalled;
					this.btnInstall.Enabled = testOk;
					this.btnWriteToLog.Enabled = testOk;
					this.btnTruncateLog.Enabled = testOk;
					this.btnViewLog.Enabled = testOk;
				}
				else
				{
					this.btnValidateConnection.Enabled = false;
					this.btnInstall.Enabled = false;
					this.btnWriteToLog.Enabled = false;
					this.btnTruncateLog.Enabled = false;
					this.btnViewLog.Enabled = false;
				}
			}
		}
		private void BuildConnectionString()
		{
			try
			{
				using (var dlg = new ConnectionStringBuilder())
				{
					dlg.LoadData(this.unMaskedConnStr);
					if (dlg.ShowDialog(this.FindForm()) == DialogResult.OK)
					{
						this.unMaskedConnStr = dlg.GetConnectionString();
						this.txtDbConfig.Text = dlg.GetConnectionStringMasked();
						this.btnValidateConnection.Enabled = false;
						this.btnInstall.Enabled = false;
						this.btnWriteToLog.Enabled = false;
						this.btnTruncateLog.Enabled = false;
						this.connStrValid = false;
						this.SetDirty();
					}
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.ToString(), HSS.Logging.Management.Resources.Title_BuildConnString, MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);
			}
		}
		private void InstallSQLServices(bool InstallOption)
		{
			this.Cursor = Cursors.WaitCursor;
			try
			{
				if (this.txtDbConfig.Text.Length > 0 && this.connStrValid)
				{
					DataAccessConnection dac = new DataAccessConnection(this.unMaskedConnStr);

					#region Ensure we have a valid Database Name (Initial Catalog)

					string title = HSS.Logging.Management.Resources.Title_InstallSqlServices;
					string err = string.Empty;

					if (string.IsNullOrEmpty(dac.InitialCatalog))
						err = HSS.Logging.Management.Resources.cantInstall_MissingDbName;
					else if (dac.InitialCatalog == masterDb)
						err = HSS.Logging.Management.Resources.cantInstall_MasterDb;
					else if (dac.InitialCatalog == defaultDb)
						err = HSS.Logging.Management.Resources.cantInstallDefaultDb;

					if (!string.IsNullOrEmpty(err))
					{
						MessageBox.Show(err, title, MessageBoxButtons.OK, MessageBoxIcon.Warning);
						return;
					}

					#endregion

					DatabaseInstaller.Show(this.FindForm(), InstallOption, dac.ConnectionString);

					this.ValidateConnection(false);

				}
				else
				{
					DatabaseInstaller.Show(this.FindForm(), InstallOption);
				}
			}
			catch { }
			finally
			{
				this.Cursor = Cursors.Default;
			}
		}
		private void InsertLog()
		{
			if (this.IsDirty)
				throw new InvalidOperationException("Cannot insert a test message until the current config settings have been saved.");

			this.Cursor = Cursors.WaitCursor;

			try
			{
				string TestMessage = string.Format(HSS.Logging.Management.Resources.msg_TestEntryMessage, Environment.UserName);
				using (ILogServerChannel channel = ChannelFactory<ILogServerChannel>.CreateChannel(this.binding, this.epa))
					channel.WriteEvent(TestMessage, Environment.UserName, "Test Message", string.Empty, "HSS.Logging.Management", string.Empty, EventLogEntryType.SuccessAudit);
				MessageBox.Show(HSS.Logging.Management.Resources.msg_TestEntry, HSS.Logging.Management.Resources.Title_InsertLog, MessageBoxButtons.OK, MessageBoxIcon.Information);
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, HSS.Logging.Management.Resources.Title_Failed, MessageBoxButtons.OK, MessageBoxIcon.Error);
			}

			this.Cursor = Cursors.Default;
		}
		private void TruncateLog()
		{
			if (this.IsDirty)
				throw new InvalidOperationException("Cannot truncate the log until the current config settings have been saved.");

			this.Cursor = Cursors.WaitCursor;


			try
			{
				using (ILogServerChannel channel = ChannelFactory<ILogServerChannel>.CreateChannel(this.binding, this.epa))
					channel.TruncateLog(0); ;
				MessageBox.Show(HSS.Logging.Management.Resources.msg_LogHasBeenTruncated, HSS.Logging.Management.Resources.Title_TruncateLog, MessageBoxButtons.OK, MessageBoxIcon.Information);
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, HSS.Logging.Management.Resources.Title_Failed, MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
			this.Cursor = Cursors.Default;
		}
		#endregion

		#region Event Handlers

		private void rdoLogTypeMachine_CheckedChanged(object sender, EventArgs e)
		{
			if (rdoLogTypeMachine.Checked && !this.Loading)
			{
				this.SetDirty();
				this.FormatUI();
			}
		}

		private void rdoLogTypeDb_CheckedChanged(object sender, EventArgs e)
		{
			if (rdoLogTypeDb.Checked && !this.Loading)
			{
				this.SetDirty();
				this.FormatUI();
			}
		}

		private void chkAutoTruncate_CheckedChanged(object sender, EventArgs e)
		{
			if (this.txtDbConfig.Text.Length > 0 && !this.Loading)
				this.SetDirty();
			this.dateTimePicker1.Enabled = this.chkAutoTruncate.Checked;
			this.nudDaysToRetain.Enabled = this.chkAutoTruncate.Checked;
		}

		private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
		{
			if (this.txtDbConfig.Text.Length > 0 && !this.Loading)
				this.SetDirty();
		}

		private void nudDaysToRetain_ValueChanged(object sender, EventArgs e)
		{
			if (this.txtDbConfig.Text.Length > 0 && !this.Loading)
				this.SetDirty();
		}

		private void btnBuild_Click(object sender, EventArgs e)
		{
			this.Cursor = Cursors.WaitCursor;
			this.btnBuild.Enabled = false;
			this.BuildConnectionString();
			this.btnBuild.Enabled = true;
			this.Cursor = Cursors.Default;
		}

		private void btnTestConnectivity_Click(object sender, EventArgs e)
		{
			this.Cursor = Cursors.WaitCursor;
			this.btnValidateConnection.Enabled = false;
			this.ValidateConnection();
		}

		private void btnWriteToLog_Click(object sender, EventArgs e)
		{
			this.Cursor = Cursors.WaitCursor;
			this.btnWriteToLog.Enabled = false;
			this.InsertLog();
			this.btnWriteToLog.Enabled = true;
			this.Cursor = Cursors.Default;
		}

		private void btnTruncateLog_Click(object sender, EventArgs e)
		{
			this.TruncateLog();
		}

		private void btnInstall_Click(object sender, EventArgs e)
		{
			this.InstallSQLServices(!this.hssCoreDbInstalled);
		}

		private void btnViewLog_Click(object sender, EventArgs e)
		{
			this.btnViewLog.Enabled = false;
			this.Cursor = Cursors.WaitCursor;
			this.ShowLogViewer();
			this.btnViewLog.Enabled = true;
			this.Cursor = Cursors.Default;
		}

		private void bgwValidateConn_DoWork(object sender, DoWorkEventArgs e)
		{
			ValidationResults res = new ValidationResults();
			using (ILogServerChannel channel = ChannelFactory<ILogServerChannel>.CreateChannel(this.binding, this.epa))
			{
				try { res = channel.ValidateDbConnection((bool)e.Argument); }
				catch (Exception ex) { res.Success = false; res.Error = ex; }
			}
			e.Result = res;
		}

		private void bgwValidateConn_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			this.HideBusyDialog();
			this.ValidateConnectionComplete(e.Result as ValidationResults);
			this.HideBusyDialog();
			this.btnValidateConnection.Enabled = true;
			this.Cursor = Cursors.Default;
		}

		#endregion
	}
	#endregion
}